//
//  StatusViewController.m
//  TwitterPlane
//
//  Created by kazuyuki takahashi on 10/01/23.
//  Copyright 2010 by invisibledesigner.com. All rights reserved.
//

#import "StatusViewController.h"
#import "Status.h"
#import "StatusListView.h"
#import "StatusView.h"
#import "TwitFormWindowController.h"

#import "NSUserDefaults+App.h"
#import "Utils.h"
#import "RegexKitLite.h"


static CGFloat sDefaultHeight = 0;
static CGFloat sTextViewMargin = 0;


/**
 * テキストの描画サイズを取得
 * http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/TextLayout/Tasks/StringHeight.html
 */
static CGFloat heightForStringDrawing(NSAttributedString *string, NSSize containerSize)
{
    //無駄な処理が多そう
    NSTextStorage *storage = [[[NSTextStorage alloc] initWithAttributedString:string] autorelease];
    NSTextContainer *container = [[[NSTextContainer alloc] initWithContainerSize:containerSize] autorelease];
    NSLayoutManager *lmanager = [[[NSLayoutManager alloc] init] autorelease];
    
    [lmanager addTextContainer:container];
    [storage addLayoutManager:lmanager];
    
    [lmanager glyphRangeForTextContainer:container];
    return [lmanager usedRectForTextContainer:container].size.height;
}

static NSDictionary *usernameAttributes()
{
    static NSDictionary *attributes = nil;
    if (attributes == nil) {
        attributes = [[NSDictionary alloc] initWithObjectsAndKeys:
                        [NSColor grayColor], NSForegroundColorAttributeName, nil];
    }
    return attributes;
}

static NSDictionary *datetimeAttributes()
{
    static NSDictionary *attributes = nil;
    if (attributes == nil) {
        attributes = [[NSDictionary alloc] initWithObjectsAndKeys:
                        [NSColor grayColor], NSForegroundColorAttributeName,
                        [NSFont controlContentFontOfSize:10], NSFontAttributeName, nil];
    }
    return attributes;
}

static NSDictionary *retweeterAttributes()
{
    return datetimeAttributes();
}


@implementation StatusViewController

@synthesize attributedText = attributedText_;


- (id)initWithStatus:(Status*)status
{
    NSParameterAssert(status != nil);
    self = [super initWithNibName:@"StatusView" bundle:nil];
    NSAssert(self != nil, @"Nib load error");
    if (self) {
        [self setRepresentedObject:status];
    }
    return self;
}

- (id)init
{
    NSAssert(NO, @"invalid initializer");
    [self release];
    return self = nil;
}

//private
- (NSAttributedString*)cachedAttributedText
{
    if (attributedTextCache_ != nil) {
        return attributedTextCache_;
    }
    
    Status *st = (Status*)[self representedObject];
    NSString *text = [st text];
    NSMutableAttributedString *attText = [[[NSMutableAttributedString alloc] initWithString:text] autorelease];
    
    const NSRange all = NSMakeRange(0, [attText length]);
    
    //フォント設定
    NSFont *font = [NSFont controlContentFontOfSize:12];
    [attText addAttribute:NSFontAttributeName value:font range:all];
    
    //文字色設定
    [attText addAttribute:NSForegroundColorAttributeName value:[NSColor whiteColor] range:all];
    
    // @usernameをリンク化
    NSRange searchRange = NSMakeRange(0, [text length]);
    NSRange atNameRange = NSMakeRange(NSNotFound, 0);
    do {
        atNameRange = [text rangeOfRegex:@"@[a-zA-Z0-9_]+" inRange:searchRange];
        if (atNameRange.location != NSNotFound) {
            NSString *username = [text substringWithRange:NSMakeRange(atNameRange.location+1, atNameRange.length-1)];
            NSURL *url = [NSURL URLWithString:[@"http://twitter.com/" stringByAppendingString:username]];
            if (url != nil) {
                [attText addAttribute:NSLinkAttributeName value:url range:atNameRange];
            }
            
            //検索範囲更新
            searchRange.location = atNameRange.location + atNameRange.length;
            searchRange.length = [text length] - searchRange.location;
        } else {
            break;
        }
    } while (1);
    
    // URLをリンク化 (URL発見正規表現はてきとう)
    // memo: 処理効率は良くないがまあだいじょうぶでしょう
    NSArray *urls = [text componentsMatchedByRegex:@"https?://[-_.!~*'()a-zA-Z0-9;/?:@&=+\\$,%#]+"];
    for (NSString *urlstr in urls) {
        NSURL *url = [NSURL URLWithString:urlstr];
        if (url != nil) {
            NSRange urlrange = [text rangeOfString:urlstr];
            [attText addAttribute:NSLinkAttributeName value:url range:urlrange];
        } else {
            NSAssert1(NO, @"invalid url: %@", urlstr);
        }
    }
    
    attributedTextCache_ = [attText retain];
    return attributedTextCache_;
}

- (void)prepareView
{
    Status *st = (Status*)[self representedObject];
    NSMutableAttributedString *atext = [[[NSMutableAttributedString alloc] init] autorelease];
    
    // ユーザ名表示方法をuserdefaultsから取得して設定
    NSString *username = usernameStringForStatusView([st user]);
    if (username != nil) {
        username = [username stringByAppendingString:@" "];
        NSAttributedString *attUsername = [[[NSAttributedString alloc] initWithString:username
                                                                           attributes:usernameAttributes()] autorelease];
        [atext appendAttributedString:attUsername];
    }
    
    [atext appendAttributedString:[self cachedAttributedText]];

    //日時表示方法をuserdefaultsから取得して設定
    NSString *dateString = dateStringForStatusView([st updatedDate]);
    if (dateString != nil) {
        dateString = [@" " stringByAppendingString:dateString];
        NSAttributedString *attDateString = [[[NSAttributedString alloc] initWithString:dateString
                                                                             attributes:datetimeAttributes()] autorelease];
        [atext appendAttributedString:attDateString];
    }
    
    //RTの場合はRTしたひとのscreennameを追加
    if ([st retweeter]) {
        NSString *rtstr = [@" RT by " stringByAppendingString:[st retweeter]];
        NSAttributedString *attRTString = [[[NSAttributedString alloc] initWithString:rtstr
                                                                           attributes:retweeterAttributes()] autorelease];
        [atext appendAttributedString:attRTString];
    }
    
    // テキストの行間を調節する
    NSMutableParagraphStyle *pstyle = [[[NSMutableParagraphStyle alloc] init] autorelease];
    [pstyle setParagraphStyle:[NSParagraphStyle defaultParagraphStyle]];
    [pstyle setLineSpacing:-1.0];
    [atext addAttribute:NSParagraphStyleAttributeName value:pstyle range:NSMakeRange(0, [atext length])];
    
    [self setAttributedText:atext];
}


- (void)refreshView
{
    NSAssert([self attributedText], @"not prepared");
    [[textView_ textStorage] setAttributedString:[self attributedText]];
    
    // viewのサイズ調整
    CGFloat height = heightForStringDrawing(attributedText_, [[textView_ textContainer] containerSize]) + sTextViewMargin;
    if (height <= sDefaultHeight) {
        height = sDefaultHeight;
    }
    
    height += kStatusListViewMargin;
    
    NSView *view = [self view];
    NSSize currentSize = [view frame].size;
    if (currentSize.height == height) {
        return;
    }
    
    currentSize.height = height;
    [view setFrameSize:currentSize];
    [view setNeedsDisplay:YES];
}


- (void)loadView
{
    [super loadView];
    
    if (sDefaultHeight == 0 || sTextViewMargin == 0) {
        sDefaultHeight = [[self view] frame].size.height;
        sTextViewMargin = sDefaultHeight - [textView_ frame].size.height;
    }
    
    //リンクテキストの色を設定
    NSColor *linkColor = [NSColor colorWithDeviceRed:0.2 green:0.5 blue:1.0 alpha:1.0];
    NSDictionary *linkAttributes = [NSDictionary dictionaryWithObject:linkColor
                                                               forKey:NSForegroundColorAttributeName];
    [textView_ setLinkTextAttributes:linkAttributes];
    
    [self prepareView];
    [self refreshView];
}

- (void)viewWasAddedToSuperView
{
    //自分をresponderchainに入れる
    NSView *v = [self view];
    NSResponder *next = [v nextResponder];
    [v setNextResponder:self];
    [self setNextResponder:next];
}

- (void)mouseDown:(NSEvent *)theEvent
{
    NSPoint p = [imageView_ convertPointFromBase:[theEvent locationInWindow]];
    if ([imageView_ hitTest:p]) {
        [self openReplyWindow:imageView_];
    }
}

- (IBAction)openReplyWindow:(id)sender
{
    Status *st = (Status*)[self representedObject];
    [[TwitFormWindowController sharedController] showWindow:self replyToStatus:st];
}

- (void)dealloc
{
    [attributedTextCache_ release];
    [attributedText_ release];
    [super dealloc];
}


@end
